package com.ubxtech.xxljob.service.impl;

import cn.hutool.core.lang.Assert;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.Method;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.ubxtech.core.exception.RRException;
import com.ubxtech.core.req.PageReq;
import com.ubxtech.xxljob.bo.JobGroupPage;
import com.ubxtech.xxljob.bo.JobInfoPage;
import com.ubxtech.xxljob.bo.XxlGroupInfo;
import com.ubxtech.xxljob.bo.XxlJobInfo;
import com.ubxtech.xxljob.properties.XxlJobProperties;
import com.ubxtech.xxljob.req.*;
import com.ubxtech.xxljob.service.XxlJobService;
import com.ubxtech.xxljob.util.Constant;
import com.ubxtech.xxljob.util.ExecutorRouteStrategyEnum;
import com.ubxtech.xxljob.util.MisfireStrategyEnum;
import com.ubxtech.xxljob.util.ScheduleTypeEnum;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
import com.xxl.job.core.glue.GlueTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

import java.net.HttpCookie;
import java.util.*;

/**
 *
 * @author Victor.Xiao
 * @since 2022-11-17 20:37
*/
@Slf4j
public class XxlJobServiceImpl implements XxlJobService {

    private XxlJobProperties xxlJobProperties;

    private int timeout = 10000;
    private String cookie;

    public XxlJobServiceImpl( XxlJobProperties xxlJobProperties) {
        this.xxlJobProperties = xxlJobProperties;
        cookie = login();
    }

    @Override
    public JobInfoPage jobPageList(PageReq<XxlJobPageReq> pageReq) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("start", pageReq.getCurrent() == 0 ? 0 : pageReq.getCurrent() - 1);
        paramMap.put("length",pageReq.getSize());
        paramMap.put("jobGroup",-1);
        paramMap.put("triggerStatus",-1);
        if (pageReq.getParams() != null) {
            if (pageReq.getParams().getJobGroup() != null && pageReq.getParams().getJobGroup() > -1) {
                paramMap.put("jobGroup",pageReq.getParams().getJobGroup());
            }
            if (pageReq.getParams().getTriggerStatus() != null && pageReq.getParams().getTriggerStatus() > -1) {
                paramMap.put("triggerStatus",pageReq.getParams().getTriggerStatus());
            }
            paramMap.put("jobDesc",pageReq.getParams().getJobDesc());
            paramMap.put("executorHandler",pageReq.getParams().getExecutorHandler());
            paramMap.put("author",pageReq.getParams().getAuthor());
        }

        String body = requestXxlJobAdminStr(Constant.jobPageListPath, paramMap, Method.GET);
        return JSON.parseObject(body, JobInfoPage.class);
    }

    /**
     * 执行器列表
     * @author Victor.Xiao
     * @since 2022-11-18 19:20
     * @param pageReq pageReq
     * @return com.ubxtech.xxljob.bo.JobGroupPage
     */
    @Override
    public JobGroupPage jobGroupPageList(PageReq<XxlJobGroupPageReq> pageReq) {
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("start",pageReq.getCurrent()-1);
        paramMap.put("length",pageReq.getSize());
        if (pageReq.getParams() != null) {
            paramMap.put("appname",pageReq.getParams().getAppname());
            paramMap.put("title",pageReq.getParams().getTitle());
        }

        String body = requestXxlJobAdminStr(Constant.jobGroupPath, paramMap, Method.POST);
        return JSON.parseObject(body, JobGroupPage.class);
    }


    private XxlJobInfo changeXxlJobInfo(XxlJobAddReq xxlJobAddReq) {
        XxlJobInfo jobInfo = new XxlJobInfo();
        ScheduleTypeEnum scheduleType = xxlJobAddReq.getScheduleType();
        if (scheduleType != null) {
            jobInfo.setScheduleType(scheduleType.name());
        }
        MisfireStrategyEnum misfireStrategy = xxlJobAddReq.getMisfireStrategyEnum();
        if (misfireStrategy != null) {
            jobInfo.setMisfireStrategy(misfireStrategy.name());
        }
        ExecutorRouteStrategyEnum executorRouteStrategy = xxlJobAddReq.getExecutorRouteStrategy();
        if (executorRouteStrategy != null) {
            jobInfo.setExecutorRouteStrategy(executorRouteStrategy.name());
        }
        ExecutorBlockStrategyEnum executorBlockStrategy = xxlJobAddReq.getExecutorBlockStrategy();
        if (executorBlockStrategy != null) {
            jobInfo.setExecutorBlockStrategy(executorBlockStrategy.name());
        }
        GlueTypeEnum glueType = xxlJobAddReq.getGlueType();
        if (glueType != null) {
            jobInfo.setGlueType(glueType.name());
        }
        BeanUtils.copyProperties(xxlJobAddReq,jobInfo);
        return jobInfo;
    }

    @Override
    public void update(XxlJobUpdateReq xxlJobUpdateReq) {
        XxlJobInfo jobInfo =changeXxlJobInfo(xxlJobUpdateReq);
        this.update(jobInfo);
    }

    @Override
    public Integer add(XxlJobAddReq xxlJobAddReq) {
        XxlJobInfo jobInfo =changeXxlJobInfo(xxlJobAddReq);
        return this.add(jobInfo);
    }

    /**
     * 批量添加job
     * @author Victor.Xiao
     * @since 2022-11-22 17:32
     * @param xxlJobAddReq xxlJobAddReq
     * @param batchAddDiffInfo batchAddDiffInfo
     * @return java.util.List<java.lang.Integer>
     */
    @Override
    public List<Integer> addBatch(XxlJobAddReq xxlJobAddReq,List<XxlJobBatchAddDiffInfoReq> batchAddDiffInfo) {
        XxlJobInfo jobInfo =changeXxlJobInfo(xxlJobAddReq);
        List<Integer> result = new ArrayList<>();
        if (!CollectionUtils.isEmpty(batchAddDiffInfo)) {
            for (XxlJobBatchAddDiffInfoReq xxlJobBatchAddDiffInfoReq : batchAddDiffInfo) {
                jobInfo.setJobDesc(xxlJobBatchAddDiffInfoReq.getJobDesc());
                jobInfo.setExecutorParam(xxlJobBatchAddDiffInfoReq.getExecutorParam());
                jobInfo.setAuthor(xxlJobBatchAddDiffInfoReq.getAuthor());
                jobInfo.setScheduleConf(xxlJobBatchAddDiffInfoReq.getScheduleConf());
                result.add(this.add(jobInfo));
            }
        }
        return result;
    }

    /**
     * 包含添加job的所有参数
     * @author Victor.Xiao
     * @since 2022-11-17 17:50
     * @param jobInfo 任务信息
     * @return java.lang.Integer 任务id
     */
    @Override
    public Integer add(XxlJobInfo jobInfo) {
        JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(jobInfo));
        String body = requestXxlJobAdminCheckCode(Constant.jobAddPath, jsonObject,Method.POST);
        ReturnT<String> returnT = JSON.parseObject(body,new TypeReference<ReturnT<String>>(){});
        //添加完直接启动
        int id = Integer.parseInt(returnT.getContent());
        this.start(id);
        return id;
    }

    @Override
    public void update(XxlJobInfo jobInfo) {
        JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(jobInfo));
        requestXxlJobAdminCheckCode(Constant.jobUpdatePath, jsonObject,Method.POST);
    }

    @Override
    public void remove(int id) {
        Map<String,Object> map = new HashMap<>();
        map.put("id",id);
        requestXxlJobAdminCheckCode(Constant.jobDeletePath, map,Method.POST);
    }
    /**
     * remove job
     * @author Victor.Xiao
     * @since 2022-11-17 19:40
     * @param ids ids
     */
    @Override
    public void remove(Collection<Integer> ids) {
        if (!CollectionUtils.isEmpty(ids)) {
            for (Integer id : ids) {
                this.remove(id);
            }
        }
    }

    /**
     * 根据条件删除
     * @author Victor.Xiao
     * @since 2022-11-22 17:45
     * @param query query
     */
    @Override
    public void remove(XxlJobPageReq query) {
        PageReq<XxlJobPageReq> pageReq = new PageReq<>();
        pageReq.setCurrent(1);
        pageReq.setSize(9999);
        pageReq.setParams(query);
        JobInfoPage jobInfoPage = this.jobPageList(pageReq);
        if (jobInfoPage != null && jobInfoPage.getData() != null && jobInfoPage.getData().size() > 0) {
            for (XxlJobInfo jobInfo : jobInfoPage.getData()) {
                this.remove(jobInfo.getId());
            }
        }
    }


    @Override
    public void start(int id) {
        Map<String,Object> map = new HashMap<>();
        map.put("id",id);
        requestXxlJobAdminCheckCode(Constant.jobStartPath, map,Method.POST);
    }

    @Override
    public void stop(int id) {
        Map<String,Object> map = new HashMap<>();
        map.put("id",id);
        requestXxlJobAdminCheckCode(Constant.jobStopPath, map,Method.POST);
    }

    @Override
    public void triggerJob(int id, String executorParam, String addressList) {
        Map<String,Object> map = new HashMap<>(3);
        map.put("id",id);
        map.put("executorParam",executorParam);
        map.put("addressList",addressList);
        requestXxlJobAdminCheckCode(Constant.jobTriggerPath, map,Method.POST);
    }

    @Override
    public List<String> nextTriggerTime(String scheduleType, String scheduleConf) {
        Map<String,Object> map = new HashMap<>();
        map.put("scheduleType",scheduleType);
        map.put("scheduleConf",scheduleConf);
        String body = requestXxlJobAdminStr(Constant.jobNextTriggerTimePath, map,Method.GET);
        ReturnT<List<String>> returnT = JSON.parseObject(body,new TypeReference<ReturnT<List<String>>>(){});
        return returnT.getContent();
    }


    private String request(String url, Map<String, Object> paramMap, Map<String, String> header, Method method) {
        HttpRequest httpRequest = null;
        if (method == Method.GET) {
            httpRequest= HttpRequest.get(url);
        } else {
            httpRequest= HttpRequest.post(url);
        }
        if (!CollectionUtils.isEmpty(header)) {
            httpRequest.headerMap(header, true);
        }
        HttpResponse response = httpRequest.form(paramMap).timeout(timeout).execute();
        int status = response.getStatus();
        Assert.isTrue(status == 200, "错误请求");
        return response.body();
    }

    private String requestXxlJobAdminStr(String url, Map<String, Object> paramMap, Method method) {
        Map<String, String> header = new HashMap<>();
        header.put("Cookie",cookie);
        String body = request(xxlJobProperties.getAdminAddresses()+""+url, paramMap, header, method);
        if (log.isDebugEnabled()) {
            log.debug("请求xxl-job-admin,返回结果:{},参数:{}",body,paramMap);
        }
        return body;
    }

    private String requestXxlJobAdminCheckCode(String url, Map<String, Object> paramMap, Method method) {
        String body = requestXxlJobAdminStr(url, paramMap, method);
        JSONObject json = JSONObject.parseObject(body);
        Integer code = json.getInteger("code");
        if (code == null) {
            throw new RRException("参数错误");
        }
        if (code != 200) {
            throw new RRException(json.getString("msg"), code);
        }
        return body;
    }


    private String login(){
        String adminUrl = xxlJobProperties.getAdminAddresses();
        String userName = xxlJobProperties.getUserName();
        String password = xxlJobProperties.getPassword();

        Map<String, Object > paramMap = new HashMap<>();
        paramMap.put("userName",userName);
        paramMap.put("password",password);

        HttpResponse httpResponse = HttpRequest.post(adminUrl+"/login").form(paramMap).timeout(timeout).execute();
        int status = httpResponse.getStatus();

        if (200 != status) {
            throw new RuntimeException("登录失败");
        }

        String body = httpResponse.body();
        ReturnT returnT = JSONObject.parseObject(body, ReturnT.class);
        if (200 != returnT.getCode()) {
            throw new RuntimeException("登录失败:"+returnT.getMsg());
        }

        String cookieName = "XXL_JOB_LOGIN_IDENTITY";
        HttpCookie cookie = httpResponse.getCookie(cookieName);
        if (cookie == null) {
            throw new RuntimeException("没有获取到登录成功的cookie，请检查登录连接或者参数是否正确");
        }
        //Cookie
        return cookieName + "=" + cookie.getValue();
    }



    /**
     * 查询当前定时job的groupId
     * @author Victor.Xiao
     * @since 2023-05-22 10:50
     * @return java.lang.Integer
     */
    @Override
    public XxlGroupInfo selectJobGroupId() {
        PageReq<XxlJobGroupPageReq> jobGroupPageReqPageReq = new PageReq<>();
        XxlJobGroupPageReq xxlJobGroupPageReq = new XxlJobGroupPageReq();
        xxlJobGroupPageReq.setAppname(xxlJobProperties.getAppName());
        jobGroupPageReqPageReq.setParams(xxlJobGroupPageReq);
        JobGroupPage jobGroupPage = this.jobGroupPageList(jobGroupPageReqPageReq);

        if (jobGroupPage != null && jobGroupPage.getData() != null && jobGroupPage.getData().size() > 0) {
            return jobGroupPage.getData().get(0);
        }
        return null;
    }

    public static void main(String[] args) {
        String str = "{\"code\":200,\"msg\":null,\"content\":\"6\"}";
        ReturnT returnT = JSON.parseObject(str,new TypeReference<ReturnT>(){});
        System.out.println(returnT);
    }

}
